<script setup lang="ts" name="PropertiesPanel">
import { useModelerStore } from '@/store/bpmnProcess/modelerStore'
import { debounce } from 'lodash-es'
import Logger from '@/utils/Logger'
import getBpmnIconType from './utils/getIconType'
import bpmnIcons from './utils/iconClass'
import EventEmitter from '@/utils/EventEmitter'
import CkIcon from '@/designer/Common/components/CkIcon/index.vue'

import { isAsynchronous } from './utils/asynchronous'
import { isJobExecutable } from './utils/jobExecution'
import { isCanBeConditional } from './utils/condition'
import { isStartInitializable } from './utils/initiator'
import { isHistoryCleanup } from './utils/historyCleanup'
import { isTasklist } from './utils/tasklist'
import { isExternalTask } from './utils/externalTask'
import { isCandidateStarter } from './utils/candidateStarter'
import { isFormSupported } from './utils/formSource'
import { areInputParametersSupported, areOutputParametersSupported } from './utils/dataInputOutput'
import { isProcessVariablesSupported } from './utils/processVariables'
import { isUserAssignmentSupported } from './utils/userAssignment'
import { isTaskListener, isExecuteListener } from './utils/listeners'
import { isMultiInstanceSupported } from './utils/multiInstance'
import { isImplementSupported } from './utils/implementationType'
import { isFieldInjectionsSupported } from './utils/fieldInjections'
import { areConnectorsSupported } from './utils/connectorInputOutput'
import { isExternalErrorsSupported, isErrorSupported, isMessageSupported, isSignalSupported, isEscalationSupported, isTimerSupported, isLinkSupported, isCompensationSupported } from './utils/bpmnPropertyUtils/eventDefinition'
import { isScriptTaskSupported } from './utils/scriptTask'
import { isCalledElementSupported } from './utils/calledElement'
import { areInMappingsSupported, areOutMappingsSupported } from './utils/mappingInOut'

import Base from './components/Base/index.vue'
import Document from './components/Document/index.vue'
import Asynchronous from './components/Asynchronous/index.vue'
import Extension from './components/Extension/index.vue'
import ExecuteListeners from './components/ExecuteListeners/index.vue'
import JobExecution from './components/JobExecution/index.vue'
import Conditional from './components/Conditional/index.vue'
import StartInitiator from './components/StartInitiator/index.vue'
import HistoryCleanup from './components/HistoryCleanup/index.vue'
import TaskList from './components/Tasklist/index.vue'
import ExternalTask from './components/ExternalTask/index.vue'
import CandidateStarter from './components/CandidateStarter/index.vue'
import FormSource from './components/FormSource/index.vue'
import DataInput from './components/DataInput/index.vue'
import DataOutput from './components/DataOutput/index.vue'
import ProcessVariables from './components/ProcessVariables/index.vue'
import UserAssignment from './components/UserAssignment/index.vue'
import TaskListeners from './components/TaskListeners/index.vue'
import MultiInstance from './components/MultiInstance/index.vue'
import ImplementationType from './components/ImplementationType/index.vue'
import FieldInjections from './components/FieldInjections/index.vue'
import ConnectorInput from './components/ConnectorInput/index.vue'
import ConnectorOutput from './components/ConnectorOutput/index.vue'
import ExternalErrors from './components/ExternalErrors/index.vue'
import Errors from './components/Errors/index.vue'
import Messages from './components/Messages/index.vue'
import Signals from './components/Signals/index.vue'
import Escalation from './components/Escalation/index.vue'
import ScriptTask from './components/ScriptTask/index.vue'
import Timer from './components/Timer/index.vue'
import Link from './components/Link/index.vue'
import Compensation from './components/Compensation/index.vue'
import CalledElement from './components/CalledElement/index.vue'
import MappingIn from './components/MappingIn/index.vue'
import MappingOut from './components/MappingOut/index.vue'

const modelerStore = useModelerStore()

const currentElementId = ref(null)
const panelHeader = ref({
  icon: '',
  type: '',
  title: ''
})

const renderComponents = shallowRef(markRaw([]))

const setCurrentComponents = (element) => {
  renderComponents.value = []
  renderComponents.value.push(Base)
  renderComponents.value.push(Document)
  isScriptTaskSupported(element) && renderComponents.value.push(ScriptTask)
  isSignalSupported(element) && renderComponents.value.push(Signals)
  isMessageSupported(element) && renderComponents.value.push(Messages)
  isEscalationSupported(element) && renderComponents.value.push(Escalation)
  isTimerSupported(element) && renderComponents.value.push(Timer)
  isLinkSupported(element) && renderComponents.value.push(Link)
  isCompensationSupported(element) && renderComponents.value.push(Compensation)
  isCalledElementSupported(element) && renderComponents.value.push(CalledElement)
  isImplementSupported(element) && renderComponents.value.push(ImplementationType)
  isMultiInstanceSupported(element) && renderComponents.value.push(MultiInstance)
  isUserAssignmentSupported(element) && renderComponents.value.push(UserAssignment)
  isFormSupported(element) && renderComponents.value.push(FormSource)
  isHistoryCleanup(element) && renderComponents.value.push(HistoryCleanup)
  isTaskListener(element) && renderComponents.value.push(TaskListeners)
  isTasklist(element) && renderComponents.value.push(TaskList)
  isCandidateStarter(element) && renderComponents.value.push(CandidateStarter)
  isCanBeConditional(element) && renderComponents.value.push(Conditional)
  isStartInitializable(element) && renderComponents.value.push(StartInitiator)
  isExternalTask(element) && renderComponents.value.push(ExternalTask)
  isExternalErrorsSupported(element) && renderComponents.value.push(ExternalErrors)
  isErrorSupported(element) && renderComponents.value.push(Errors)
  isAsynchronous(element) && renderComponents.value.push(Asynchronous)
  areInMappingsSupported(element) && renderComponents.value.push(MappingIn)
  areInputParametersSupported(element) && renderComponents.value.push(DataInput)
  areConnectorsSupported(element) && renderComponents.value.push(ConnectorInput)
  areOutMappingsSupported(element) && renderComponents.value.push(MappingOut)
  areOutputParametersSupported(element) && renderComponents.value.push(DataOutput)
  areConnectorsSupported(element) && renderComponents.value.push(ConnectorOutput)
  isJobExecutable(element) && renderComponents.value.push(JobExecution)
  isProcessVariablesSupported(element) && renderComponents.value.push(ProcessVariables)
  isExecuteListener(element) && renderComponents.value.push(ExecuteListeners)
  renderComponents.value.push(Extension)
  isFieldInjectionsSupported(element) && renderComponents.value.push(FieldInjections)
}

const setCurrentElement = debounce((element) => {
  let activatedElement = element
  let activatedElementTypeName = ''

  if (!activatedElement) {
    activatedElement = modelerStore.getModeler.get('elementRegistry')?.find((el) => el.type === 'bpmn:Process') || modelerStore.getModeler.get('elementRegistry')?.find((el) => el.type === 'bpmn:Collaboration')
    if (!activatedElement) {
      return Logger.prettyError('未找到元素！')
    }
  }
  Logger.prettyInfo('activatedElement', activatedElement)
  activatedElementTypeName = getBpmnIconType(activatedElement)
  modelerStore.setActiveElement(markRaw(activatedElement))
  currentElementId.value = activatedElement.id
  panelHeader.value.type = modelerStore.getModeler?.get('translate')(activatedElement.type.split(':')[1])
  panelHeader.value.title = modelerStore.getModeler?.get('translate')(activatedElementTypeName)
  panelHeader.value.icon = bpmnIcons[activatedElementTypeName]
  Logger.prettyPrimary('panelHeader', panelHeader.value)

  setCurrentComponents(activatedElement)
  EventEmitter.emit('element-update', activatedElement)
}, 100)

// 事件总线监听
EventEmitter.on('modeler-init', (modeler) => {
  // 导入完成后默认选中 process 节点
  modeler.on('import.done', () => {
    console.log('初始化完成事件')
    setCurrentElement(null)
  })
  // 监听选择事件，修改当前激活的元素以及表单
  modeler.on('selection.changed', ({ newSelection }) => {
    console.log('节点选中事件')
    if (newSelection.length > 0) {
      setCurrentElement(newSelection[0])
    } else {
      setCurrentElement(null)
    }
  })
  modeler.on('element.changed', ({ element }) => {
    console.log('节点改变事件')
    // 保证 修改 "默认流转路径" 等类似需要修改多个元素的事件发生的时候，更新表单的元素与原选中元素不一致。
    if (element) {
      if (element.id === currentElementId.value) {
        setCurrentElement(element)
      }
    }
  })

  modeler.on('element.click', (event) => {
    Logger.prettyInfo('Element Click', event)
  })
  // 节点删除事件
  modeler.on('shape.removed', () => {
    currentElementId.value = null
  })
  // 连线删除事件
  modeler.on('connection.removed', () => {
    currentElementId.value = null
  })
})

onMounted(() => {
  if (!currentElementId.value) {
    setCurrentElement(null)
  }
})
</script>

<template>
  <div class="properties-panel">
    <div class="panel-header">
      <CkIcon :icon="'bp:' + panelHeader.icon"></CkIcon>
      <h2>{{ panelHeader.title }}</h2>
    </div>
    <div class="panel-body">
      <el-scrollbar v-if="renderComponents.length > 0">
        <el-form :label-width="90" label-position="right">
          <el-collapse>
            <template v-for="item, index in renderComponents" :key="index">
              <component :is="item"></component>
            </template>
          </el-collapse>
        </el-form>
      </el-scrollbar>
    </div>
  </div>
</template>

<style scoped lang="scss">
.properties-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 480px;
  border-left: 1px solid var(--el-border-color-lighter);

  .panel-header {
    display: flex;
    align-items: center;
    padding: 12px;

    .el-icon {
      font-size: 32px !important;
    }

    h2 {
      margin-left: 10px;
      font-weight: bold;
      font-size: 16px;
    }
  }

  .panel-body {
    position: relative;
    flex: 1;
    overflow: hidden;

    .el-scrollbar {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
    }
  }
}
</style>
